/*
 * Copyright 2003-2005 (c) Live Media Pty Ltd. <argot@einet.com.au> 
 *
 * This software is licensed under the Argot Public License 
 * which may be found in the file LICENSE distributed 
 * with this software.
 *
 * More information about this license can be found at
 * http://www.einet.com.au/License
 * 
 * The Developer of this software is Live Media Pty Ltd,
 * PO Box 4591, Melbourne 3001, Australia.  The license is subject 
 * to the law of Victoria, Australia, and subject to exclusive 
 * jurisdiction of the Victorian courts.
 */

#include "../Types.h"

#include "../dictionary/Dictionary.h"

#include "../common/BigEndianUnsignedShort.h"
#include "../common/BigEndianUnsignedByte.h"
#include "../meta/MetaName.h"
#include "../meta/MetaExpression.h"
#include "../meta/MetaEnvelop.h"

#include "../util/chunkbuffer.h"
#include "../DynamicTypeMap.h"

static int writeCoreMap( type_output_stream * out, reference_type_map * core );


int write_message( type_library * library, argot_write writer, void * writerData, type_name * name, void * object )
{
	type_map refCore;
	dynamic_type_map core;
	dynamic_type_map dataMap;
	type_output_stream messageStream;
	type_output_stream typeOutStream;
	chunk_buffer dataBuffer;
	chunk_buffer dictionaryBuffer;
	chunk_buffer metaBuffer;
	int rc;
	int id;
	int dataSize;

	/* Create the basic core map. */
	type_map_init( &refCore, library );
	type_map_core_init( (reference_type_map*) &refCore );		
	type_map_map( &refCore, 22, type_library_get_id( library, &dictionary_map_name ));
	type_map_map( &refCore, 23, type_library_get_id( library, &dictionary_words_name ));
	type_map_map( &refCore, 24, type_library_get_id( library, &dictionary_definition_name ));
	type_map_map( &refCore, 25, type_library_get_id( library, &dictionary_entry_name ));
	type_map_map( &refCore, 26, type_library_get_id( library, &meta_envelop_name ));
	type_map_map( &refCore, 27, type_library_get_id( library, &meta_definition_envelop_name ));

	/* prepare meta type map for meta dictionary part of file. */
	dynamic_type_map_init( &core, library, &refCore );
	type_map_core_init( (reference_type_map*) &core );
	type_map_map( &core, 22, type_library_get_id( library, &dictionary_map_name ));
	type_map_map( &core, 23, type_library_get_id( library, &dictionary_words_name ));
	type_map_map( &core, 24, type_library_get_id( library, &dictionary_definition_name ));
	type_map_map( &core, 25, type_library_get_id( library, &dictionary_entry_name ));
	type_map_map( &core, 26, type_library_get_id( library, &meta_envelop_name ));
	type_map_map( &core, 27, type_library_get_id( library, &meta_definition_envelop_name ));
	
	/* create a dynamic type map for data dictionary. */
	dynamic_type_map_init_reflect( &dataMap, library ); 

	/* write out data */
	chunk_buffer_init( &dataBuffer );
	type_output_init( &typeOutStream, (type_map*) &dataMap, &chunkBufferWriter, &dataBuffer );

	id = type_map_get_mapped_id( &dataMap, type_library_get_id( library, name ) );
	if ( id <= NOTYPE ) return id;

	rc = type_output_write_named_object( &typeOutStream, &uint16_name, (void *) &id );
	rc = type_output_write_named_object( &typeOutStream, name, object );
	if ( rc != TYPE_OK )
	{
		chunk_buffer_clean( &dataBuffer );
		type_output_destroy( &typeOutStream );
		return rc;
	}

	type_output_destroy( &typeOutStream );
	chunk_buffer_close( &dataBuffer );

	/* write out dictionary */
	/* writing out the dictionary may dynamically assign new types */
	/* simple solution is to write it twice */
	reference_type_map_set_reference( (reference_type_map*)&core, (type_map*)&dataMap );
	chunk_buffer_init( &dictionaryBuffer );
	type_output_init( &typeOutStream, (type_map*) &core, &chunkBufferWriter, &dictionaryBuffer );
	
	id = 1;
	rc = type_output_write_named_object( &typeOutStream,&uint8_name, (void *) &id );
	rc = type_output_write_named_object( &typeOutStream, &dictionary_words_name, &dataMap );
	if ( rc != TYPE_OK )
	{
		chunk_buffer_clean( &dataBuffer );
		chunk_buffer_clean( &dictionaryBuffer );
		type_output_destroy( &typeOutStream );
		return rc;
	}
	
	type_output_destroy( &typeOutStream );
	chunk_buffer_close( &dictionaryBuffer );
	chunk_buffer_clean( &dictionaryBuffer );
	
	chunk_buffer_init( &dictionaryBuffer );
	type_output_init( &typeOutStream, (type_map*) &core, &chunkBufferWriter, &dictionaryBuffer );
	
	id = 1;
	rc = type_output_write_named_object( &typeOutStream,&uint8_name, (void *) &id );
	rc = type_output_write_named_object( &typeOutStream, &dictionary_words_name, &dataMap );
	if ( rc != TYPE_OK )
	{
		chunk_buffer_clean( &dataBuffer );
		chunk_buffer_clean( &dictionaryBuffer );
		type_output_destroy( &typeOutStream );
		return rc;
	}
	
	type_output_destroy( &typeOutStream );
	chunk_buffer_close( &dictionaryBuffer );

	/* write out the meta dictionary core used to write the message dictionary */
	reference_type_map_set_reference( (reference_type_map*)&core, (type_map*)&refCore );
	chunk_buffer_init( &metaBuffer );
	type_output_init( &typeOutStream, (type_map*) &core, &chunkBufferWriter, &metaBuffer );
	writeCoreMap( &typeOutStream, (reference_type_map*) &core );	
	type_output_destroy( &typeOutStream );
	chunk_buffer_close( &metaBuffer );
	

    /* write out the message ( meta, dictionary, data ) */
	type_output_init( &messageStream, (type_map*) &refCore, writer, writerData );
    
	dataSize = chunk_buffer_length( &metaBuffer );	
	rc = chunk_buffer_write_stream( &metaBuffer, 0, dataSize, &messageStream );
	if ( rc != dataSize )
	{
		return TYPE_EOF;
	}
	chunk_buffer_clean( &metaBuffer );

	dataSize = chunk_buffer_length( &dictionaryBuffer );	
	rc = chunk_buffer_write_stream( &dictionaryBuffer, 0, dataSize, &messageStream );
	if ( rc != dataSize )
	{
		return TYPE_EOF;
	}
	chunk_buffer_clean( &dictionaryBuffer );
    
	dataSize = chunk_buffer_length( &dataBuffer );	
	rc = chunk_buffer_write_stream( &dataBuffer, 0, dataSize, &messageStream );
	if ( rc != dataSize )
	{
		return TYPE_EOF;
	}
	chunk_buffer_clean( &dataBuffer );

	return TYPE_OK;
}

static int writeCoreMap( type_output_stream * out, reference_type_map * core )
{
	int * coreIds;
	int size;
	int x;
	int rc;
	chunk_buffer coreBuffer;
	chunk_buffer extensionBuffer;
	type_output_stream typeOutStream;
	int id;
	int dataSize;
	int extensionCount = 0;
	type_map_iterator iter;

	id = 2;
	rc = type_output_write_named_object( out, &uint8_name, (void *) &id );
	if ( rc != TYPE_OK ) return -1;
	
	/* write out the core elements. */
	chunk_buffer_init( &coreBuffer );
	type_output_init( &typeOutStream, (type_map*) core, &chunkBufferWriter, &coreBuffer );
	
	
	type_map_core_get_identifiers( map, &coreIds, &size );
	
	/* write the number of entries. */
	rc = type_output_write_named_object( &typeOutStream, &uint16_name, &size );
	if ( rc != TYPE_OK ) return -1;

	for ( x=0 ; x<size; x++ )
	{
		int id = coreIds[x];
		type_name * name = type_map_get_name( &core->map, id );
		type_element * definition = type_map_get_structure( &core->map, id );
	
		rc = type_output_write_named_object( &typeOutStream, &uint16_name, &id );
		if ( rc != TYPE_OK ) return rc;
		rc = type_output_write_named_object( &typeOutStream, &meta_name_name, name );
		if ( rc != TYPE_OK ) return rc;		
		rc = type_output_write_named_object( &typeOutStream, &dictionary_definition_name, definition );
		if ( rc != TYPE_OK ) return rc;		
	}

	chunk_buffer_close( &coreBuffer );
	dataSize = chunk_buffer_length( &coreBuffer );
	
	type_output_write_named_object( out, &uint16_name, (void *) &dataSize );	
	chunk_buffer_write_stream( &coreBuffer, 0, dataSize, out );


	/* write out the extensions */
	chunk_buffer_init( &extensionBuffer );
	type_output_init( &typeOutStream, (type_map*) core, &chunkBufferWriter, &extensionBuffer );

	/* count the number of extensions */
	type_map_iterator_init( &iter, (type_map*) core );
	while( type_map_iterator_next( &iter ) )
	{
		bool found = false;
		int id = type_map_iterator_id( &iter );
		for ( x=0 ; x< size; x++ )
		{
			if ( coreIds[x] == id )
			{
				found = true;
				break;
			}
		}
		
		if ( !found )
		{
			extensionCount++;
		}
	}
	
	type_output_write_named_object( &typeOutStream, &uint16_name, (void *) &extensionCount );	

	/* write out the extensions */
	type_map_iterator_init( &iter, (type_map*) core );
	while( type_map_iterator_next( &iter ) )
	{
		bool found = false;
		int id = type_map_iterator_id( &iter );
		type_name * name;
		type_element * definition;

		for ( x=0 ; x< size; x++ )
		{
			if ( coreIds[x] == id )
			{
				found = true;
				break;
			}
		}
		
		/* don't write out core types */
		if ( found )
		{
			continue;
		}
		
		name = type_map_get_name( &core->map, id );
		definition = type_map_get_structure( &core->map, id );
	
		rc = type_output_write_named_object( &typeOutStream, &uint16_name, &id );
		if ( rc != TYPE_OK ) return rc;
		rc = type_output_write_named_object( &typeOutStream, &meta_name_name, name );
		if ( rc != TYPE_OK ) return rc;		
		rc = type_output_write_named_object( &typeOutStream, &dictionary_definition_name, definition );
		if ( rc != TYPE_OK ) return rc;		
		
	}	
	
	chunk_buffer_close( &coreBuffer );
	dataSize = chunk_buffer_length( &extensionBuffer );
	
	type_output_write_named_object( out, &uint16_name, &dataSize );	
	chunk_buffer_write_stream( &extensionBuffer, 0, dataSize, out );

	return TYPE_OK;	
}

